/*
 * Copyright (c) 2009 DeledImporter
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'Pyro Games' nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.pyrogames.jme;

import com.jme.light.DirectionalLight;
import com.jme.light.Light;
import com.jme.light.LightNode;
import com.jme.light.PointLight;
import com.jme.light.SpotLight;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.state.LightState;

import net.pyrogames.deled.geometry.DXSNode;
import net.pyrogames.deled.geometry.DXSPrimitive;
import net.pyrogames.deled.geometry.DXSSpatial;
import net.pyrogames.deled.geometry.DXSPrimitive.PolygonWind;
import net.pyrogames.deled.light.DXSDirectionalLight;
import net.pyrogames.deled.light.DXSLight;
import net.pyrogames.deled.light.DXSPointLight;
import net.pyrogames.deled.light.DXSSpotLight;
import net.pyrogames.jme.extension.DXSLightNode;

/**
 * An extension of the JME2Format class that (surprise)
 * is able to parse custom elements made available from
 * importer extensions. Those extensions are:
 * <p>
 * <code>-LightNodeExtension</code>
 * <p>
 * <code>-AttachmentExtension</code>
 * <p>
 * <code>-CollisionVolumeExtension</code>
 * 
 * @author Nicholas Woodfield
 *
 */
public class ExtendedJME2Format extends JME2Format {
	private LightState lightState;
	
	@Override
	public void loadData(DXSNode root){
		rootNode = convertDXSNode(root);
		lightState = display.getRenderer().createLightState();
		lightState.setEnabled(true);
		//Go through the root's children
		for(DXSSpatial s : root.getChildren()){
			//If we hit a light node
			if(s instanceof DXSLightNode){
				//Create a new light node
				LightNode lightNode = new LightNode(s.getName());
				//Convert the light
				Light jmeLight = convertDXSLight(((DXSLightNode)s).getLight());
				//Set the node's location if applicable
				if(jmeLight instanceof PointLight){
					lightNode.getLocalTranslation().set(((PointLight) jmeLight).getLocation());
				}
				//Now we need to set the local rotation if the light has a direction
				if(jmeLight instanceof SpotLight){
					Quaternion q = new Quaternion();
					Vector3f v1 = lightNode.getLocalRotation().getRotationColumn(2);
					Vector3f v2 = ((SpotLight) jmeLight).getDirection();
					q.fromAngleNormalAxis(v2.angleBetween(v1), v2.cross(v1));
					lightNode.setLocalRotation(q);
				} else if(jmeLight instanceof DirectionalLight){
					Quaternion q = new Quaternion();
					Vector3f v1 = lightNode.getLocalRotation().getRotationColumn(2);
					Vector3f v2 = ((DirectionalLight) jmeLight).getDirection();
					q.fromAngleNormalAxis(v2.angleBetween(v1), v2.cross(v1));
					lightNode.setLocalRotation(q);
				}
				//Attach light to the node, and attach to the lightstate of the model root
				lightNode.setLight(jmeLight);
				rootNode.attachChild(lightNode);
				lightState.attach(jmeLight);
				//If we hit a node, we need to go through it and it's children
			} else if(s instanceof DXSNode){
				convertChildren((DXSNode)s, rootNode);
				//We hit a mesh and need to convert it
			} else if(s instanceof DXSPrimitive){
				rootNode.attachChild(convertDXSPrimitive((DXSPrimitive)s, PolygonWind.CounterClockWise));
				//We hit a light and must attach it to the root
			}
		}
		//Finally set the scale for this data
		rootNode.setLocalScale(scale);
		rootNode.setRenderState(lightState);
		rootNode.updateRenderState();
	}
	
	@Override
	public void convertChildren(DXSNode n, Node parent){
		//Convert the given dxs node
		Node child = convertDXSNode(n);
		//Now convert the children
		for(DXSSpatial s : n.getChildren()){
			//If we hit a light node
			if(s instanceof DXSLightNode){
				//Create a new light node
				LightNode lightNode = new LightNode(s.getName());
				//Convert the light
				Light jmeLight = convertDXSLight(((DXSLightNode)s).getLight());
				//Set the node's location if applicable
				if(jmeLight instanceof PointLight){
					lightNode.getLocalTranslation().set(((PointLight) jmeLight).getLocation());
				}
				//Now we need to set the local rotation if the light has a direction
				if(jmeLight instanceof SpotLight){
					Quaternion q = new Quaternion();
					Vector3f v1 = lightNode.getLocalRotation().getRotationColumn(2);
					Vector3f v2 = ((SpotLight) jmeLight).getDirection();
					q.fromAngleNormalAxis(v2.angleBetween(v1), v2.cross(v1));
					lightNode.setLocalRotation(q);
				} else if(jmeLight instanceof DirectionalLight){
					Quaternion q = new Quaternion();
					Vector3f v1 = lightNode.getLocalRotation().getRotationColumn(2);
					Vector3f v2 = ((DirectionalLight) jmeLight).getDirection();
					q.fromAngleNormalAxis(v2.angleBetween(v1), v2.cross(v1));
					lightNode.setLocalRotation(q);
				}
				//Attach light to the node, and attach to the lightstate of the model root
				lightNode.setLight(jmeLight);
				rootNode.attachChild(lightNode);
				lightState.attach(jmeLight);
				//If we hit a node, we need to go through it and it's children
			} else if(s instanceof DXSNode){
				convertChildren((DXSNode)s, child);
				//Otherwise if its a primitive, convert and attach
			} else if(s instanceof DXSPrimitive){
				child.attachChild(convertDXSPrimitive((DXSPrimitive)s,PolygonWind.CounterClockWise));
			}
		}
		//Once we're done, attach to the parent node
		parent.attachChild(child);
	}
	
	@Override
	public Light convertDXSLight(DXSLight l){
		Light light = null;
		if(l instanceof DXSSpotLight){
			light = new SpotLight();
			
			//Set colors
			light.setAmbient(convertDXSColorRGBA(l.getAmbient()));
			light.setDiffuse(convertDXSColorRGBA(l.getDiffuse()));
			light.setSpecular(convertDXSColorRGBA(l.getSpecular()));
			
			//Set values
			light.setConstant(l.getConstant());
			light.setLinear(l.getLinear());
			light.setQuadratic(l.getQuadratic());
			
			//Set true/false
			light.setAttenuate(l.isAttenuate());
			light.setEnabled(l.isEnabled());
			light.setShadowCaster(l.isEnabled());
			
			//Set specific
			Vector3f v = convertDXSVector3f(((DXSSpotLight)l).getLocation());
			
			//Do not scale the location, since our light node will do that for us
			
			((SpotLight)light).setLocation(v);
			((SpotLight)light).setDirection(convertDXSVector3f(((DXSSpotLight)l).getDirection()));
			
			//Is this actually the exponent? (we parse the "falloff" in the file)
			((SpotLight)light).setExponent(((DXSSpotLight)l).getExponent());
			
			//Outer or inner angle?
			((SpotLight)light).setAngle(((DXSSpotLight)l).getOuterAngle());
			
		} else if(l instanceof DXSPointLight){
			light = new PointLight();
			
			//Set colors
			light.setAmbient(convertDXSColorRGBA(l.getAmbient()));
			light.setDiffuse(convertDXSColorRGBA(l.getDiffuse()));
			light.setSpecular(convertDXSColorRGBA(l.getSpecular()));
			
			//Set values
			light.setConstant(l.getConstant());
			light.setLinear(l.getLinear());
			light.setQuadratic(l.getQuadratic());
			
			//Set true/false
			light.setAttenuate(l.isAttenuate());
			light.setEnabled(l.isEnabled());
			light.setShadowCaster(l.isEnabled());
			
			//Set specific
			Vector3f v = convertDXSVector3f(((DXSPointLight)l).getLocation());
			
			//Do not scale the location, since our light node will do that for us
			
			((PointLight)light).setLocation(v);
		} else if(l instanceof DXSDirectionalLight){
			light = new DirectionalLight();
			
			//Set colors
			light.setAmbient(convertDXSColorRGBA(l.getAmbient()));
			light.setDiffuse(convertDXSColorRGBA(l.getDiffuse()));
			light.setSpecular(convertDXSColorRGBA(l.getSpecular()));
			
			//Set true/false
			light.setAttenuate(l.isAttenuate());
			light.setEnabled(l.isEnabled());
			light.setShadowCaster(l.isEnabled());
			
			//Set values
			light.setConstant(l.getConstant());
			light.setLinear(l.getLinear());
			light.setQuadratic(l.getQuadratic());
			
			//Set specific
			((DirectionalLight)light).setDirection(convertDXSVector3f(((DXSDirectionalLight)l).getDirection()));
		}
		return light;
	}
}
